#ifndef unigame_Transform_h
#define unigame_Transform_h

#include "Vector2.h"
#include "Vector3.h"
#include "Vector4.h"
#include "Matrix.h"
#include "Matrix4x4.h"
#include "Quaternion.h"
#include "Plane.h"

namespace unigame
{
	enum TransformFlag
	{
		E_POSITION_DIRTY_TFF = 1,
		E_OTHER_DIRTY_TFF = 1 << 1,
		E_LOCALE_WORLD_DIRTY_TFF = 1 << 2,
	};

	class GameObject;
	class Transform
	{
	public:
		Transform():m_dirtyFlag(0), m_gameObject(0), m_scale(1, 1, 1), m_isWorldLocaleDirty(true){}
		~Transform();

		void update();

		void init(GameObject* go)
		{
			m_gameObject = go;
		}

		void getLocalPosition(Vector3f& v)
		{
			v.x = m_position.x;
			v.y = m_position.y;
			v.z = m_position.z;
		}

		void setLocalPosition(const Vector3f& v)
		{
			setLocalPosition(v.x, v.y, v.z);
		}

		void setLocalPosition(float x, float y, float z)
		{
			setFlag(E_POSITION_DIRTY_TFF);
			m_position.x = x;
			m_position.y = y;
			m_position.z = z;
		}

		void getLocalScale(Vector3f& v)
		{
			v.x = m_scale.x;
			v.y = m_scale.y;
			v.z = m_scale.z;
		}

		void setLocalScale(const Vector3f& v)
		{
			setLocalScale(v.x, v.y, v.z);
		}

		void setLocalScale(float x, float y, float z)
		{
			setFlag(E_OTHER_DIRTY_TFF);
			m_scale.x = x;
			m_scale.y = y;
			m_scale.z = z;
		}

		void getLocalRotation(Quaternion& q)
		{
			q.x = m_rotation.x;
			q.y = m_rotation.y;
			q.z = m_rotation.z;
			q.w = m_rotation.w;
		}

		void setLocalRotation(const Quaternion& q)
		{
			m_rotation = q;
		}

		void setLocalRotation(const Vector3f& v, const float angle)
		{
			setFlag(E_OTHER_DIRTY_TFF);
			m_rotation.set(v, angle);
		}

		void setLocalRotation(const Vector3f& v)
		{
			setFlag(E_OTHER_DIRTY_TFF);
			m_rotation.setEulerByRad(v);
		}

		void setLocalRotationByRad(const float x, const float y, const float z)
		{
			setFlag(E_OTHER_DIRTY_TFF);
			m_rotation.setEulerByRad(x, y , z);
		}

		void setLocalRotationByAngle(const float x, const float y, const float z)
		{
			setLocalRotationByRad(x * MULT_TO_RAD, y * MULT_TO_RAD, z * MULT_TO_RAD);
		}

		Matrixf& getLocaleToWorld()
		{
			return m_localeToWorld;
		}

		Matrixf& getWorldToLocale()
		{
			if(m_isWorldLocaleDirty)
			{
				m_isWorldLocaleDirty = false;
				m_localeToWorld.inverse(m_worldToLocale);
			}
			return m_worldToLocale;
		}

		void onAddToFather();
		void onRemoveFromFather(GameObject* father);

		void resetFlag()
		{
			m_dirtyFlag = 0;
		}

		char dirtyFlag()
		{
			return m_dirtyFlag;
		}

		void setFlag(int32u flag)
		{
			m_dirtyFlag |= flag;
		}

		void clearFlag(int32u flag)
		{
			m_dirtyFlag &= ~flag;
		}

		bool getFlag(int32u flag)
		{
			return (m_dirtyFlag & flag) != 0;
		}
	private:
		void makeLocalMatrix();
		void makeLocaleToWorld();

	private:
		Matrixf m_worldToLocale;
		Matrixf m_localeToWorld;
		Matrixf m_locale;

		Quaternion m_rotation;
		Vector3f m_position;
		Vector3f m_scale;
		
		GameObject* m_gameObject;

		char m_dirtyFlag;
		bool m_isWorldLocaleDirty;
	};
}
#endif